package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.wemedia.WmMediaConstans;
import com.heima.common.exception.CustException;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import jdk.nashorn.internal.parser.JSONParser;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.helper.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import javax.xml.crypto.Data;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    @Value("${file.oss.web-site}")
    String webSite;

    /**
     * 查询所有自媒体文章
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findList(WmNewsPageReqDto dto) {
        // 非空判断
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.checkParam();
        // 条件封装执行查询
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        // 文章标题不为空就按标题模糊查询
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            wrapper.like(WmNews::getTitle, dto.getKeyword());
        }
        // 频道id不为空就按照频道查询
        if (dto.getChannelId() != null) {
            wrapper.eq(WmNews::getChannelId, dto.getChannelId());
        }
        // 文章状态不为空就按照状态查询
        if (dto.getStatus() != null) {
            wrapper.eq(WmNews::getStatus, dto.getStatus());
        }
        // 发布时间不为空按照时间查询
        if (dto.getBeginPubdate() != null && dto.getEndPubdate() != null) {
            wrapper.between(WmNews::getPublishTime, dto.getBeginPubdate(), dto.getBeginPubdate());
        }
        // 当前自媒体人文章
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        wrapper.eq(WmNews::getUserId, user.getId());
        // 按照创建日期倒序
        wrapper.orderByDesc(WmNews::getCreatedTime);
        // 分页条件构建
        Page<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        // 执行查询
        IPage<WmNews> pageResult = page(page, wrapper);
        // 返回封装查询结果
        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), pageResult.getTotal(), pageResult.getRecords());


        // 处理文章图片
        responseResult.setHost(webSite);
        return responseResult;
    }

    // 注入 Kfka
    @Autowired
    KafkaTemplate<String, String> kafkaTemplate;

    /**
     * 发布文章(保存草稿/发布)
     *
     * @param wmNewsDto
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    @Override
    public ResponseResult submitNews(WmNewsDto wmNewsDto) throws InvocationTargetException, IllegalAccessException {

        //参数校验
        if (wmNewsDto == null || StringUtils.isBlank(wmNewsDto.getContent())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章内容不能为空");
        }

        Short status = wmNewsDto.getStatus();
        if (status == null || (status.intValue() != 0 && status.intValue() != 1)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章状态错误");
        }
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }

        //  保存或修改文章
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(wmNewsDto, wmNews);

        wmNews.setUserId(user.getId()); // 发表用户

        // 封面图片集合处理
        List<String> imagesList = wmNewsDto.getImages();
        if (imagesList != null && imagesList.size() > 0) {
            // 图片集合替换图片前缀
            String str = imagesToStr(imagesList);
            wmNews.setImages(str);
        }
        //  如果文章布局是自动,需要设置为null
        if (wmNewsDto.getType().equals(WmMediaConstans.WM_NEWS_TYPE_AUTO)) {
            wmNews.setType(null);
        }
        wmNews.setUserId(user.getId()); // 用户

        // 2.保存或修改 wmNews文章
        seaveOrUpdatewmNews(wmNews);

        // 如果是草稿不用处理关联关系
        if (wmNewsDto.getStatus().equals(WmMediaConstans.WM_NEWS_DRAFT_STATUS)) {
            return ResponseResult.okResult();
        }
        // 3.保存文章和素材的关联关系
        // TODO:抽取文章内容涉及的图片
        List<Map> listMap = JSONArray.parseArray(wmNewsDto.getContent(), Map.class);
        List<String> contentUrlList = parseContentImages(listMap);

        // TODO: 保存文章内容和素材关联关系
        if (contentUrlList != null && contentUrlList.size() > 0) {
            //保存内容中图片和素材的关联关系
            saveRelativeInfo(wmNews.getId(), contentUrlList, WmMediaConstans.WM_CONTENT_REFERENCE);
        }
        // TODO: 关联文章封面中的图片和素材关系 封面可能是选择自动或者无图
        saveRelativeInfoForCover(wmNewsDto, wmNews, contentUrlList);
        // 发送自动审核消息通知
        Map map = new HashMap();
        map.put("wmNewsId", wmNews.getId());
        kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, JSON.toJSONString(map));
        log.info("自动审核的消息通知成功发送  主题: {} 文章id: {}", NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, wmNews.getId());

        return ResponseResult.okResult();
    }

    /**
     * 根据id删除文章
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult delNews(Integer id) {
        if (id == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "缺少文章id");
        }

        WmNews wmNews = getById(id);
        if (wmNews == null) {

            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        //   使用了枚举类 如果等于 9
        if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode()) && wmNews.getEnable().equals(WmMediaConstans.WM_NEWS_UP)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章已经发布,无法删除");
        }

        // 去除文章和素材之间的关联关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));
        //5.删除文章
        removeById(wmNews.getId());
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

    }

    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {

        if (dto == null || dto.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 查询文章
        WmNews wmNews = getById(dto.getId());
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        // 判断文章状态是否发布
        if (!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "当前文章不是发布状态，不能上下架");
        }

        // 修改文章状态，同步到app端（后期做）TODO
        if (dto.getEnable() != null && dto.getEnable() > -1 && dto.getEnable() < 2) {
            update(Wrappers.<WmNews>lambdaUpdate().eq(WmNews::getId, dto.getId())
                    .set(WmNews::getEnable, dto.getEnable()));
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 查询待发布文章id列表
     *
     * @return
     */
    @Override
    public ResponseResult<List<Integer>> findRelease() {

        // 查询 状态 4 或 8 并且发布时间小于等于当前时间
        List<WmNews> list = list(Wrappers.<WmNews>lambdaQuery().in(WmNews::getStatus, 8, 4)
                .le(WmNews::getPublishTime, new Date())
                .select(WmNews::getId));// 只查询wmnews 的 id字段
        List<Integer> newsIds = list.stream().map(wmNews -> wmNews.getId()).collect(Collectors.toList());

        return ResponseResult.okResult(newsIds);
    }

    /**
     * 处理封面图片和素材表的关系
     *
     * @param wmNewsDto      前台入参
     * @param wmNews         文章信息
     * @param contentUrlList 内容图片列表
     */
    private void saveRelativeInfoForCover(WmNewsDto wmNewsDto, WmNews wmNews, List<String> contentUrlList) {
        //1.获取封面图片
        List<String> images = wmNewsDto.getImages();
        //2. 如果dto type  -1 代表自动生成封面
        if (WmMediaConstans.WM_NEWS_TYPE_AUTO.equals(wmNewsDto.getType())) {
            if (contentUrlList == null || contentUrlList.size() == 0) {
                //  0
                wmNews.setType(WmMediaConstans.WM_NEWS_NONE_IMAGE);

                //单图 1
            } else if (contentUrlList.size() > 0 && contentUrlList.size() <= 2) {
                wmNews.setType(WmMediaConstans.WM_NEWS_SINGLE_IMAGE);
                //收集图片数据
                images = contentUrlList.stream().limit(1).collect(Collectors.toList());
            } else {
                //  多图
                wmNews.setType(WmMediaConstans.WM_NEWS_MANY_IMAGE);
                //收集图片数据
                images = contentUrlList.stream().limit(3).collect(Collectors.toList());
            }
            if (images != null && images.size() > 0) {
                wmNews.setImages(imagesToStr(images));
                //更新文章
                updateById(wmNews);

            }
        }
        //保存封面与素材的关联关系
        if (images != null && images.size() > 0) {
            images = images.stream().map(url -> url.replaceAll(webSite, "")).collect(Collectors.toList());
            saveRelativeInfo(wmNews.getId(), images, WmMediaConstans.WM_IMAGE_REFERENCE);
        }

    }


    @Autowired
    WmMaterialMapper wmMaterialMapper;

    /**
     * 保存文章内容 和 素材关联关系
     * 文章中url必须在素材表中存在,不在则保存失败
     *
     * @param newsId       文章id
     * @param materialUrls 素材url列表
     * @param refType      引用类型 0 内容引用, 1,封面引用
     */
    private void saveRelativeInfo(Integer newsId, List<String> materialUrls, Short refType) {
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(
                Wrappers.<WmMaterial>lambdaQuery().eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId())
                        .in(WmMaterial::getUrl, materialUrls)
        );
        if (wmMaterials == null || wmMaterials.size() == 0) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "关联的指定素材不存在");
        }
        // 提取素材列表 id 和  url
        Map<String, Integer> urlAndIdMap = wmMaterials.stream()
                .collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
        // 判断传入的素材 是否全部存在 如果在 收集每个素材相对应的id
        List<Integer> materialIds = new ArrayList<>();
        materialUrls.forEach((url) -> {
            if (!urlAndIdMap.containsKey(url)) {
                CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            materialIds.add(urlAndIdMap.get(url));
        });
        //保存关联关系
        wmNewsMaterialMapper.saveRelations(materialIds, newsId, refType);
    }

    /**
     * 抽取文章内容涉及的图片
     *
     * @param contentMaps
     * @return
     */
    private List<String> parseContentImages(List<Map> contentMaps) {
        // list 集合中保留 type 是 images的集合
        Stream<Map> mapStream = contentMaps.stream();
        List<String> stringList = mapStream.filter(map -> WmMediaConstans.WM_NEWS_TYPE_IMAGE.equals(map.get("type")))
                //获取value得到url
                .map(map -> (String) map.get("value"))
                // 替换路径前缀
                .map(url -> url.replaceAll(webSite, ""))
                .collect(Collectors.toList());

        return stringList;

    }

    @Autowired
    WmNewsMaterialMapper wmNewsMaterialMapper;


    /**
     * 保存修改文章
     *
     * @param wmNews 文章对象(前端传递)
     */
    private void seaveOrUpdatewmNews(WmNews wmNews) {
        // 补全wmnews信息 创建时间
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        // 判断id是否为空
        if (wmNews.getId() == null) {
            //为空  直接保存
            save(wmNews);
        } else {
            // 不为空,先删除 素材和文章关系, 在修改文章
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId, wmNews.getId()));
            // 修改
            updateById(wmNews);
        }
    }

    /**
     * 将图片集合替换图片前缀
     *
     * @param imagesList
     * @return
     */
    private String imagesToStr(List<String> imagesList) {

        String collect = imagesList.stream()
                .map(str -> str.replaceAll(webSite, ""))
                .collect(Collectors.joining(","));

        return collect;
    }

}